package com.pic.daoimpl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jdbc.Work;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.pic.dao.BaseDao;
import com.pic.entity.PageResults;

/**
 * BaseDao 的实现类
 * Created by AI-MASK on 2014/9/4.
 */
public class BaseDaoImpl<T, ID extends Serializable> implements BaseDao<T,ID> {
    @Autowired
    private SessionFactory sessionFactory;
    protected Class<T> clazz; //

    public BaseDaoImpl() {
        super();
        clazz = getEntityClass(super.getClass());
    }

    protected Class getEntityClass(Class clazz) {
        Type tp = clazz.getGenericSuperclass();

        if (tp instanceof ParameterizedType) {
            clazz = (Class) ((ParameterizedType) tp).getActualTypeArguments()[0];
            return clazz;
        } else {
            throw new ClassCastException("this is not ParameterizedType type");
        }
    }
    /**
     * <保存实体>
     * <完整保存实体>
     * @param t 实体参数
     * @see com.beyondnet.bpms.dao.BaseDao#save(Object)
     */
    @Override
    public Serializable save(T t) {
    	return this.getSession().save(t);
    }

    /**
     * <保存或者更新实体>
     * @param t 实体
     * @see com.beyondnet.bpms.dao.BaseDao#saveOrUpdate(Object)
     */
    @Override
    public void saveOrUpdate(T t) {
        this.getSession().saveOrUpdate(t);
    }

    /**
     * <refresh>
     * @param t 实体
     * @see com.beyondnet.bpms.dao.BaseDao#refresh(Object)
     */
    @Override
    public void refresh(T t) {
        this.getSession().refresh(t);
    }

    /**
     * <update>
     * @param t 实体
     * @see com.beyondnet.bpms.dao.BaseDao#update(Object)
     */
    @Override
    public void update(T t) {
        this.getSession().update(t);
    }
    /**
     * <根据ID更新实体>
     * @param Id 实体id
     * @return 是否更新成功
     * @see com.beyondnet.bpms.dao.BaseDao#updateById(java.io.Serializable)
     */
    @Override
    public boolean updateById(ID Id) {
    	T t = get(Id);
        if(t == null){
            return false;
        }
        update(t);
        return true;
    }
    /**
     * <load>
     * <加载实体的load方法>
     * @param id 实体的id
     * @return 查询出来的实体
     * @see com.beyondnet.bpms.dao.BaseDao#load(java.io.Serializable)
     */
    @Override
    public T load(ID id) {
        T load = (T) this.getSession().load(clazz, id);
        return load;
    }

    /**
     * <get>
     * <查找的get方法>
     * @param id 实体的id
     * @return 查询出来的实体
     * @see com.beyondnet.bpms.dao.BaseDao#get(java.io.Serializable)
     */
    @Override
    public T get(ID id) {
        T load = (T) this.getSession().get(clazz, id);
        return load;
    }

    /**
     * 检查该实例与此相关
     * <contains>
     * @param t 实体
     * @return 是否包含
     * @see com.beyondnet.bpms.dao.BaseDao#contains(Object)
     */
    @Override
    public boolean contains(T t) {
        return this.getSession().contains(t);
    }

    /**
     * <delete>
     * <删除表中的t数据>
     * @param t 实体
     * @see com.beyondnet.bpms.dao.BaseDao#delete(Object)
     */
    @Override
    public void delete(T t) {
        this.getSession().delete(t);
    }

    /**
     * <根据ID删除数据>
     * @param Id 实体id
     * @return 是否删除成功
     * @see com.beyondnet.bpms.dao.BaseDao#deleteById(java.io.Serializable)
     */
    @Override
    public boolean deleteById(ID Id) {
        T t = get(Id);
        if(t == null){
            return false;
        }
        delete(t);
        return true;
    }

    /**
     * <删除所有>
     * @param entities 实体的Collection集合
     * @see com.beyondnet.bpms.dao.BaseDao#deleteAll(java.util.Collection)
     */
    @Override
    public void deleteAll(Collection<?> entities) {
        for(Object entity : entities) {
            this.getSession().delete(entity);
        }
    }

    /**
     * <执行Hql语句>
     * @param hqlString hql
     * @param values 不定参数数组
     * @see com.beyondnet.bpms.dao.BaseDao#queryHql(String, Object[])
     */
    @Override
    public boolean queryHql(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        int b = query.executeUpdate();
        return b > 0 ?true:false;
    }

    /**
     * <执行Sql语句>
     * @param sqlString sql
     * @param values 不定参数数组
     * @see com.beyondnet.bpms.dao.BaseDao#querySql(String, Object[])
     */
    @Override
    public void querySql(String sqlString, Object... values) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        query.executeUpdate();
    }

    /**
     * <根据HQL语句查找唯一实体>
     * @param hqlString HQL语句
     * @param values 不定参数的Object数组
     * @return 查询实体
     * @see com.beyondnet.bpms.dao.BaseDao#getByHQL(String, Object[])
     */
    @Override
    public T getByHQL(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return (T) query.uniqueResult();
    }

    /**
     * <根据SQL语句查找唯一实体>
     * @param sqlString SQL语句
     * @param values 不定参数的Object数组
     * @return 查询实体
     * @see com.beyondnet.bpms.dao.BaseDao#getBySQL(String, Object[])
     */
    @Override
    public T getBySQL(String sqlString, Object... values) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return (T) query.uniqueResult();
    }

    /**
     * <根据HQL语句，得到对应的list>
     * @param hqlString HQL语句
     * @param values 不定参数的Object数组
     * @return 查询多个实体的List集合
     * @see com.beyondnet.bpms.dao.BaseDao#getListByHQL(String, Object[])
     */
    @Override
    @Transactional
    public List<T> getListByHQL(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.list();
    }

    /**
     * <根据SQL语句，得到对应的list>
     * @param sqlString HQL语句
     * @param values 不定参数的Object数组
     * @return 查询多个实体的List集合
     * @see com.beyondnet.bpms.dao.BaseDao#getListBySQL(String, Object[])
     */
    @Override
    public List<T> getListBySQL(String sqlString, Object... values ) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.list();
    }

    /**
     * 由sql语句得到List
     * @param sql
     * @param map
     * @param values
     * @return List
     * @see com.beyondnet.bpms.dao.BaseDao#findListBySql(String, com.beyondnet.bpms.mapper.RowMapper, Object...)
     */
    @Override
    public List findListBySql(final String sql, final RowMapper map, final Object... values) {
        final List list = new ArrayList();
        // 执行JDBC的数据批量保存
        Work jdbcWork = new Work() {
            public void execute(Connection connection) throws SQLException {
                PreparedStatement ps = null;
                ResultSet rs = null;
                try {
                    ps = connection.prepareStatement(sql);
                    for (int i = 0; i < values.length; i++) {
                        setParameter(ps, i, values[i]);
                    }

                    rs = ps.executeQuery();
                    int index = 0;
                    while (rs.next()) {
                        Object obj = map.mapRow(rs, index++);
                        list.add(obj);

                    }
                }
                finally {
                    if (rs != null) {
                        rs.close();
                    }
                    if (ps != null) {
                        ps.close();
                    }
                }
            }
        };
        this.getSession().doWork(jdbcWork);
        return list;
    }

    /**
     * <根据HQL得到记录数>
     * @param hql HQL语句
     * @param values 不定参数的Object数组
     * @return 记录总数
     * @see com.beyondnet.bpms.dao.BaseDao#countByHql(String, Object[])
     */
    @Override
    public Long countByHql(String hql, Object... values) {
        Query query = this.getSession().createQuery(hql);
        if(values != null){
            for(int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return (Long) query.uniqueResult();
    }

    /**
     * <HQL分页查询>
     * @param hql HQL语句
     * @param countHql 查询记录条数的HQL语句
     * @param pageNo 下一页开始记录
     * @param pageSize 一页总条数
     * @param values 不定Object数组参数
     * @return PageResults的封装类，里面包含了页码的信息以及查询的数据List集合
     * @see com.beyondnet.bpms.dao.BaseDao#findPageByFetchedHql(String, String, int, int, Object[])
     */
    @Override
    public PageResults<T> findPageByFetchedHql(String hql, String countHql, int pageNo, int pageSize, Object... values) {
        PageResults<T> retValue = new PageResults<T>();
        Query query = this.getSession().createQuery(hql);
        if(values != null){
            for(int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
//        int currentPage = pageNo > 1 ? pageNo : 1;
        int currentPage = pageNo == 0 ? 1:(pageNo / pageSize) +1 ;
        retValue.setCurrentPage(currentPage);
        retValue.setPageSize(pageSize);
        if (countHql == null) {
            ScrollableResults results = query.scroll();
            results.last();
            retValue.setTotalCount(results.getRowNumber() + 1);// 设置总记录数
        }
        else {
            Long count = countByHql(countHql, values);
            retValue.setTotalCount(count.intValue());
        }
        List<T> itemList = query.setFirstResult(pageNo).setMaxResults(pageSize).list();
        if (itemList == null) {
            itemList = new ArrayList<T>();
        }
        retValue.setResults(itemList);

        return retValue;
    }

    /**
     * 按属性查找对象列表，匹配方式为相等
     * @param propertyName
     * @param value
     * @return
     */
    public List<T> list(String propertyName, Object value) {

        Criterion criterion = Restrictions.like(propertyName, "%" + value + "%");
        return list(criterion);
    }

    /**
     * 根据查询条件获取数据列表
     * @param criterion
     * @return
     */
    private List<T> list(Criterion criterion) {
        Criteria criteria = createCriteria();
        criteria.add(criterion);
        return criteria.list();
    }

    /**
     * 获取对象列表根据Criteria
     * @param criteria
     * @return
     */
    public List<T> list(Criteria criteria) {
        return criteria.list();
    }
    /**
     * 根据Criterion条件创建Criteria
     * @param criterions 数量可变的Criterion
     *
     */
    public Criteria createCriteria(Criterion... criterions) {
        Criteria criteria = createCriteria();
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    /**
     * 分页查询Criteria
     * @param criteria
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<T> findPage(Criteria criteria, int pageNo, int pageSize) {
        // 设置起始结果数
        criteria.setFirstResult((pageNo - 1) * pageSize);
        // 返回的最大结果集
        criteria.setMaxResults(pageSize);
        return list(criteria);
    }
    /**
     * 获取PO总数(默认为entityClass)
    */
    public int countAll() {
        Criteria criteria = createCriteria();
        return Integer.valueOf(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
    }
    /**
     * 根据Criteria查询条件，获取PO总数
    */
    public int countAll(Criteria criteria) {

        return Integer.valueOf(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
    }

    /**
     * 分页查询Criteria
     * @param criteria
     * @param pageNo
     * @param pageSize
     * @return
     */
//    public PageResults<T> pagedQuery(Criteria criteria, int pageNo, int pageSize) {
//        Assert.isTrue(pageNo >= 1, "pageNO should start from 1");
//        // 返回查询结果集
//        List<T> list = findPage(criteria, pageNo, pageSize);
//        /*
//         * 注：因为finaPage方法改变了查询条件导致countALL方法查询为空， 所以必须重新设置setFirstResult为0
//        */
//        criteria.setFirstResult(0);
//        // count查询
//        // 获得查询总数
//        long totalCount = countAll(criteria);
//        if (totalCount < 1) {
//            return new Page<T>();
//        }
//        // 实际查询返回分页对象
//        int startIndex = Page.getStartOfPage(pageNo, pageSize);
//        return new Page<T>(startIndex, totalCount, pageSize, list);
//    }

    /**
     * 分页查询Criteria
     * @param conditionQuery
     * @param orderBy
     * @param pageNo
     * @param pageSize
     * @return
     */
//    public PageResults<T> pagedQuery(ConditionQuery conditionQuery, OrderBy orderBy,int pageNo, int pageSize) {
//
//        Assert.isTrue(pageNo >= 1,"pageNO should start from 1");
//        Criteria criteria = createCriteria();
//        // 构造查询条件和排序
//        conditionQuery.build(criteria);
//        orderBy.build(criteria);
//        // count查询
//        // 获得查询总数
//        long totalCount = countAll(criteria);
//        if (totalCount < 1) {
//            return new Page<T>();
//        }
//        // 实际查询返回分页对象
//        int startIndex = Page.getStartOfPage(pageNo, pageSize);
//        // 返回查询结果集
//        List<T> list = findPage(criteria, pageSize, pageNo);
//        return new Page<T>(startIndex, totalCount, pageSize, list);
//    }
    /**
     * 创建Criteria实例
     * @return
     */
    public Criteria createCriteria() {
        return getSession().createCriteria(clazz);
    }
    /**
     * @return the sessionFactory
     */
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * @param sessionFactory the sessionFactory to set
     */
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * @return session
     */
    public Session getSession() {
        //需要开启事务，才能得到CurrentSession
        return sessionFactory.getCurrentSession();
    }

    /**
     *
     * 设置每行批处理参数
     *
     * @param ps
     * @param pos ?占位符索引，从0开始
     * @param data
     * @throws java.sql.SQLException
     * @see [类、类#方法、类#成员]
     */
    private void setParameter(PreparedStatement ps, int pos, Object data) throws SQLException {
        if (data == null) {
            ps.setNull(pos + 1, Types.VARCHAR);
            return;
        }
        Class dataCls = data.getClass();
        if (String.class.equals(dataCls)) {
            ps.setString(pos + 1, (String)data);
        }
        else if (boolean.class.equals(dataCls)) {
            ps.setBoolean(pos + 1, ((Boolean)data));
        }
        else if (int.class.equals(dataCls)) {
            ps.setInt(pos + 1, (Integer)data);
        }
        else if (double.class.equals(dataCls)) {
            ps.setDouble(pos + 1, (Double)data);
        }
        else if (Date.class.equals(dataCls)) {
            Date val = (Date)data;
            ps.setTimestamp(pos + 1, new Timestamp(val.getTime()));
        }
        else if (BigDecimal.class.equals(dataCls)) {
            ps.setBigDecimal(pos + 1, (BigDecimal)data);
        }
        else {
            // 未知类型
            ps.setObject(pos + 1, data);
        }

    }

}

